package com.example.bjsfpost.controllers;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.event.SyncReadListener;
import com.alibaba.fastjson.JSONObject;
import com.example.bjsfpost.domain.*;
import com.example.bjsfpost.sap.SapCommonResponseDO;
import com.example.bjsfpost.sap.TestServiceImpl;
import com.example.bjsfpost.services.PostParamService;
import com.example.bjsfpost.utils.EasyExcelContext;
import com.example.bjsfpost.utils.HttpUtil;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/bjsf")
@Slf4j
public class BjsfApiPostController {
    private static final ConcurrentHashMap<String,Long> TIME_HASH_MAP = new ConcurrentHashMap();
    @Resource
    private PostParamService postParamService;

    public final AtomicInteger NUM = new AtomicInteger(0);
    public final static ConcurrentHashMap<String,Boolean> successExistsMap = new ConcurrentHashMap();
    public final static ConcurrentHashMap<String,Boolean> existsMap = new ConcurrentHashMap();
    public static AtomicInteger CHATGPT_MSG_PREFIX_NUM = new AtomicInteger(0);
    public static ThreadPoolExecutor MSG_HANDLER_THREAD_POOL ;
    public static ExecutorService WULIAO_HANDLER_THREAD_POOL ;
    public static Executor cacheExecutor ;

    static String[] NAME_MAPPING_ARRAY = new String[]{null,"1-电气件","1-结构件","1-印制板","2-插件","2-结构件","25-固化","25-插件","26计划物料","3-成品","4-装置","7-工程非生产","7-标准件","5-标准屏柜","5-订单屏柜","59-装配设备","1-外购件"};

    public final static ConcurrentLinkedQueue QUEUE = new ConcurrentLinkedQueue();
    static {
        MSG_HANDLER_THREAD_POOL = new ThreadPoolExecutor(3, 3, 1,TimeUnit.MINUTES, new LinkedBlockingDeque<>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "BjsfApiPost_" + CHATGPT_MSG_PREFIX_NUM.getAndIncrement());
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());

        WULIAO_HANDLER_THREAD_POOL = Executors.newFixedThreadPool(16);

        cacheExecutor = Executors.newCachedThreadPool();
    }


    @PostMapping("/pushData")
    public ParamPostResponseDO pushData(@RequestBody ParamPostRequestDO requestDO){
        ParamPostResponseDO result = new ParamPostResponseDO();
        result.setSuccess(true);
        result.setCode("00000");
        if(requestDO.getVersion().contains("A")){
            result.setData(ParamPostResponseDO.ReturnData.ok("ok"));
        }else{
            result.setData(ParamPostResponseDO.ReturnData.fail("error"));
        }
        return result;
    }
    @PostMapping("/selfPushData")
    public ParamPostResponseDO selfPushData(@RequestBody ParamPostRequestDO requestDO){
        Map<String, String> headers = new HashMap<>();
        headers.put("COntent-Type","application/json");
        String result = HttpUtil.sendPost("http://localhost:9890/bjsf/pushData", headers, JSONObject.toJSONString(requestDO));
        return JSONObject.parseObject(result,ParamPostResponseDO.class);
    }
    public static String SUCCESS = "SUCCESS";
    public static String ERROR = "ERROR";
    public static final AtomicBoolean proccessAto = new AtomicBoolean(false);
    @Value("${wuliao.file.path:C:\\Users\\Administrator\\Desktop\\test-batch\\v2}")
    private String wuliaoAbsolutePath;
    @Value("${wuliao.push.url:http://192.198.30.44:7002/rest/v1/notification/item}")
    private String wuliaoPushDataUrl;
    @Value("${bom.file.path:C:\\Users\\Administrator\\Desktop\\test-batch\\v2}")
    private String bomAbsolutePath;
    @Value("${bom.push.url:http://192.198.30.44:7002/rest/v1/notification/bomRelease}")
    private String bomPushDataUrl;
    @PostMapping("/dataWuLiao")
    public ParamPostResponseDO dataWuLiao(){
        log.info("===============dataWuLiao Start ==========================");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        postParamService.handlerAllWuliaoExcelByFileName(wuliaoAbsolutePath,Arrays.asList("handler","bom4"),wuliaoPushDataUrl);
//        postParamService.handlerAllWuliaoExcelByFileName("D:\\JAVAPLM二开文档\\test",Arrays.asList("handler.xlsx"));
        stopWatch.stop();
        log.info("耗时=={}秒",stopWatch.getLastTaskTimeMillis()/1000);
        log.info("===============dataWuLiao End ==========================");
        return  ParamPostResponseDO.ok();
    }
    @PostMapping("/dataBom")
    public ParamPostResponseDO dataBom(){
        log.info("===============dataBom Start ==========================");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        postParamService.handlerAllWuliaoExcelByFileName(bomAbsolutePath,Arrays.asList("handler"),bomPushDataUrl);
//        postParamService.handlerAllWuliaoExcelByFileName("D:\\JAVAPLM二开文档\\test",Arrays.asList("handler.xlsx"));
        stopWatch.stop();
        log.info("耗时=={}秒",stopWatch.getLastTaskTimeMillis()/1000);
        log.info("===============dataBom End ==========================");
        return  ParamPostResponseDO.ok();
    }
    @PostMapping("/bom4")
    public ParamPostResponseDO bom4(){
        log.info("===============data Start ==========================");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<PostParamDO> postParamDOS = postParamService.queryAll4Bom("");
        stopWatch.stop();
        log.info("耗时=={}秒",stopWatch.getLastTaskTimeMillis()/1000);
        log.info("===============data End ==========================");
        return  ParamPostResponseDO.ok(postParamDOS.size());
    }
    @PostMapping("/data")
    public ParamPostResponseDO data(){
        log.info("===============data Start ==========================");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<ErrorExcelData> resultList = new ArrayList<>();
        final String currentPath = System.getProperty("user.dir");
        final String existPath = currentPath + File.separator + "handler.xlsx";
        final String existPathTemp = currentPath + File.separator + "handler_temp.xlsx";
        //读取文件获取所有已经处理成功的数据
        File existPathfile = new File(existPath);
        File existPathTempFile = new File(existPathTemp);
        final List<ErrorExcelData> existList = existPathfile.exists() ? EasyExcelFactory.read(existPath, ErrorExcelData.class,
                new SyncReadListener()).headRowNumber(1).sheet(0).doReadSync() : new ArrayList<>();
        log.info("existList size==={}",existList.size());
        List<String> existAndSuccessList = existList.stream().filter(data -> Objects.equals(data.getSuccessFlag(), SUCCESS)).map(ErrorExcelData::getJson).collect(Collectors.toList());
        for (String s : existAndSuccessList) {
            successExistsMap.put(s,true);
        }
        try {
            //核心
            resultList.addAll(pageHandler2());
            log.info("resultList size==={}",resultList.size());
            StringBuffer dowloadFilePath = new StringBuffer(currentPath).append(File.separator).append(generatorTime()+"_Records.xlsx");
            EasyExcelFactory.write(dowloadFilePath.toString()).registerWriteHandler(EasyExcelContext.DEFAULT_EXPORT_STYLE_STRATEGY)
                    .sheet(0).head(ErrorExcelData.class).doWrite(resultList);
            if (existPathfile.exists()){
                // 第二次按照原有格式，不需要表头，追加写入
                EasyExcel.write(existPathfile, ErrorExcelData.class).needHead(false).
                        withTemplate(existPathfile).file(existPathTempFile).sheet().doWrite(resultList);
            }else {
                // 第一次写入需要表头
                EasyExcel.write(existPathfile,ErrorExcelData.class).sheet().doWrite(resultList);
            }
            if (existPathTempFile.exists()){
                existPathfile.delete();
                existPathTempFile.renameTo(existPathfile);
            }

        }catch (Exception e){
            log.error("===========================严重错误============{}",e);
        }
        stopWatch.stop();
        log.info("耗时=={}秒",stopWatch.getLastTaskTimeMillis()/1000);
        log.info("===============data End ==========================");
        return  ParamPostResponseDO.ok();
    }

    private List<ErrorExcelData> pageHandler2() {
        //即将处理的数据
        List<ParamPostRequestDO> selectList = generatorList(1000000);
        final int totalSize = selectList.size();

        if(CollectionUtils.isEmpty(selectList)){
            throw new RuntimeException("查询数据库为空，请检查配置再试");
        }
        final ReentrantLock reentrantLock = new ReentrantLock();
        final Condition condition = reentrantLock.newCondition();
        //处理请求
        selectList.forEach(paramPostRequestDO->{
            MSG_HANDLER_THREAD_POOL.submit(()-> handlerThreadM(paramPostRequestDO,"http://192.198.30.44:7002/rest/v1/notification/item"));
        });
        List<ErrorExcelData> resultList = new ArrayList<>(selectList.size());
        new Thread(()->{
            while(true){
                int size = QUEUE.size();
                System.out.println("QUEUE size == " + size );
                log.info("============== QUEUE size == " + size );
                try {
                    Thread.sleep(10_000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if(totalSize<=size){
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    if(totalSize<=size || expireTime()){
                        reentrantLock.lock();
                        condition.signalAll();
                        reentrantLock.unlock();
                        break;
                    }
                }
            }
        },"listener-queue").start();
        reentrantLock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            reentrantLock.unlock();
        }
        resultList.addAll(QUEUE);
        QUEUE.clear();
        return resultList;
    }

    /**
     * 超过两分钟，自动解锁
     * @return
     */
    private static boolean expireTime() {
        long l = System.currentTimeMillis();
        Long bjsf_api_expire = TIME_HASH_MAP.get("BJSF_API_EXPIRE");
        if(null == bjsf_api_expire || bjsf_api_expire <= 0 ){
            bjsf_api_expire = l;
            TIME_HASH_MAP.put("BJSF_API_EXPIRE",bjsf_api_expire);
            return false;
        }
        Long min2 = 2*60*1000L;
        if( (l - bjsf_api_expire) >= min2){
            TIME_HASH_MAP.put("BJSF_API_EXPIRE",l);
            return true;
        }
        TIME_HASH_MAP.put("BJSF_API_EXPIRE",l);
        return false;
    }

    public static ErrorExcelData handlerThreadMTest(ParamPostRequestDO requestDO) {
        int i = RandomUtil.randomInt(0, 100);
        ErrorExcelData errorExcelData;
        if(i > 60 ){
            errorExcelData = ErrorExcelData.successButRepeat(requestDO);
        }else {
            errorExcelData = ErrorExcelData.noSuccessButRepeat(requestDO);
        }
        QUEUE.offer(errorExcelData);
        return errorExcelData;
    }
    public static ErrorExcelData handlerThreadM(ParamPostRequestDO requestDO,String url) {
        //处理请求
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type","application/json");
        String requestBody = JSONObject.toJSONString(requestDO);
        if(successExistsMap.containsKey(requestBody)){
            ErrorExcelData errorExcelData = ErrorExcelData.successButRepeat(requestDO);
            QUEUE.offer(errorExcelData);
            return errorExcelData;
        }else if(existsMap.containsKey(requestBody)){
            ErrorExcelData errorExcelData = ErrorExcelData.noSuccessButRepeat(requestDO);
            QUEUE.offer(errorExcelData);
            return errorExcelData;
        }
        existsMap.put(requestBody,true);
//        String response = HttpOrHttpsUtil.INSTANCE.doPost("http://localhost:9890/bjsf/pushData", headers, requestBody);
        ErrorExcelData errorExcelData = new ErrorExcelData();
        BeanUtils.copyProperties(requestDO,errorExcelData);
        errorExcelData.setErrorMessage("http error ");
        errorExcelData.setSuccessFlag(ERROR);
        errorExcelData.setJson(JSONObject.toJSONString(requestDO));
        try{
            String response = HttpUtil.INSTANCE.sendPost(url, headers, requestBody);
            ParamPostResponseDO paramPostResponseDO = JSONObject.parseObject(response, ParamPostResponseDO.class);
            //处理成功
            if(Objects.nonNull(paramPostResponseDO) && Objects.equals(paramPostResponseDO.getSuccess(),true)){
                boolean successFlag = Objects.nonNull(paramPostResponseDO.getData()) && Objects.equals(paramPostResponseDO.getData().getRETURN_SUBRC(),0)
                        ? true : false;
                errorExcelData.setSuccessFlag(successFlag?SUCCESS:ERROR);
                errorExcelData.setErrorMessage(successFlag?"":JSONObject.toJSONString(paramPostResponseDO.getData()));
                if(paramPostResponseDO.getSuccess()){
                    successExistsMap.put(requestBody,true);
                }else{
                    existsMap.put(requestBody,true);
                }
                QUEUE.offer(errorExcelData);
                return errorExcelData;
            }
            QUEUE.offer(errorExcelData);
            return errorExcelData;
        }catch (Exception e){
            log.error("HandlerThread == {}",e);
            errorExcelData.setErrorMessage(e.getMessage());
            QUEUE.offer(errorExcelData);
            return errorExcelData;
        }
    }

    private String generatorTime() {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
        return sf.format(new Date());
    }

    static String[] arraStr = new String[]{"A","B","C"};
    private List<ParamPostRequestDO> generatorList(Integer total){
        final List<ParamPostRequestDO> resultList = new ArrayList<>();
        /*for(int i = 0; i < total ; i++){
            ParamPostRequestDO requestDO = new ParamPostRequestDO();
            requestDO.setItemId("ID_" + RandomUtil.randomString(4));
            requestDO.setVersion(arraStr[RandomUtil.randomInt(0,3)]  + "." + RandomUtil.randomInt(0,100));
            resultList.add(requestDO);
        }
        return resultList;*/
        File file = new File("C:\\Users\\Administrator\\Desktop\\test-batch");
        if(file.exists()){
            File[] files = file.listFiles();
            for (File file1 : files) {
//                if(file1.isFile() && file1.getName().contains("2023-11-23_16-46-44_5_wuliao.xlsx")){
//                if(file1.isFile() && file1.getName().contains("2023-11-23_16-46-45_3_wuliao.xlsx")){
                if(file1.isFile() && file1.getName().contains("2023-11-23_16-56-27_10_wuliao.xlsx")){
                    List<PostParamDO> existList = EasyExcelFactory.read(file1.getAbsolutePath(), PostParamDO.class,
                            new SyncReadListener()).headRowNumber(1).sheet(0).doReadSync();
//                    System.out.println(file1.getName() + " existList size==={}" + existList.size());
                    log.info("文档 {} excel existList size==={}",file1.getName(),existList.size());
                    resultList.addAll(existList.stream().map(data->{ParamPostRequestDO request = new ParamPostRequestDO();BeanUtils.copyProperties(data,request);return request;}).collect(Collectors.toList()));
                }
            }
        }
        return resultList;
    }

    @PostMapping("/queryPage")
    public NormalResponseDO queryPage(@RequestBody PageParam req){
        PageInfo pageInfo = postParamService.handlerData(req.getPageNum(), req.getPageSize());
        return NormalResponseDO.ok(pageInfo);
    }

    @PostMapping("/etlWuliaoXls")
    public NormalResponseDO etlWuliaoXls(){
        log.info("=============== 物料Excel导出开始 ===============");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        postParamService.handlerEtlWuliaoXls();
        stopWatch.stop();
        log.info("=============== 物料Excel导出结束 ===============");
        log.info("=============== 物料Excel导出结束耗时 stopWatch=={} 秒" , stopWatch.getLastTaskTimeMillis()/1000);
        return NormalResponseDO.ok(null);

    }


    @Resource
    private TestServiceImpl testService;
    @PostMapping("/test2")
    public NormalResponseDO test2(){
        testService.test("abc");
        return NormalResponseDO.ok(null);

    }
    @PostMapping("/sql2E")
    public SapCommonResponseDO executeSql2Excel(){
        return  postParamService.executeSql2Excel();

    }
}
